﻿using Foundation.Core;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.ChangeTracking;
using Microsoft.EntityFrameworkCore.Metadata;

namespace Foundation.EntityFrameworkCore
{
    /// <summary>
    /// 根据配置判断是否重置
    /// </summary>
    public class EntityStateResetOptionsTracker : EntityStateResetTracker, IEntityStateResetTrackingInitializer, ITransientDependency
    {
        private bool _hasInitialization = false;

        private bool _entityTypesIsEmpty = true;

        protected EntityResetTrackingOptions Options { get; set; }

        public void SetOptions(EntityResetTrackingOptions options)
        {
            if (_hasInitialization)
            {
                var ex = new AppException("参数配置只能设置一次！");
                SetInnerException(ex);
                throw ex;
            }

            this.Options = options ?? throw new ArgumentNullException(nameof(options));

            _hasInitialization = true;

            _entityTypesIsEmpty = options.EntityTypes.IsNullOrEmpty();
        }

        protected override bool IsTracKingEntry(EntityEntry entry)
        {
            return !TrackingEntities.ContainsKey(entry.Entity)
                && (_entityTypesIsEmpty
                    || Options.EntityTypes.Contains(entry.Metadata.ClrType)
                    || (Options.IncludeUnchangedEntity
                    || (Options.ResetHasRowVerionEntity && EntityHasConcurrencyToken(entry.Metadata)))
                    );
        }

        protected override bool CanHandleEntry(EntityEntry entry)
        {
            if (entry.State == EntityState.Detached) return false;

            return (TrackingEntities.ContainsKey(entry.Entity) || (!Options.Entities.IsNullOrEmpty() && Options.Entities.Contains(entry.Entity)))
                && ((Options.IncludeUnchangedEntity ? TrackingStates : ChangedStates).Contains(entry.State)
                    || (!Options.IncludeUnchangedEntity && Options.ResetHasRowVerionEntity && EntityHasConcurrencyToken(entry.Metadata)))
                && (_entityTypesIsEmpty || Options.EntityTypes.Contains(entry.Metadata.ClrType));
        }

        protected override bool CanHandleException(Exception exception, DbContext dbContext, bool isFromSaveChanges = false)
        {
            return (!isFromSaveChanges || Options.AutoResetOnSaveChangesFailed)
                && (Options.IncludeUnchangedEntity
                    || Options.ResetHasRowVerionEntity
                    || _entityTypesIsEmpty
                    || Options.EntityTypes.Any(t => dbContext.Model.FindEntityType(t) != null));
        }

        /// <summary>
        /// 判断实体类型是否有行版本号
        /// </summary>
        /// <param name="entityType"></param>
        /// <returns></returns>
        protected static bool EntityHasConcurrencyToken(IEntityType entityType)
        {
            const string key = "$_HasConcurrencyToken";
            var value = entityType[key];
            if (value == null)
            {
                value = entityType.GetProperties().Any(p => p.IsConcurrencyToken);
                (entityType as IMutableAnnotatable)[key] = value;
            }

            return (bool)value;
        }
    }
}
